#include <stdio.h>
#include <string.h>
#include <errno.h>
#include <malloc.h>

#include <ijpege_common.h>
#ifdef DMALLOC
#include <imp/dmalloc.h>
#endif

static int ijpege_validate_parameters(ijpege_t *h)
{
	/* soc, buf, device manage */
	if (h->param.soc < C_M200 || h->param.soc >= C_SOC_MAX) {
		ijpege_log(h, C_LOG_ERROR, "unsupported soc:%d\n", h->param.soc);
		goto err_soc;
	}

	if ((h->param.i_width < 256) || (h->param.i_height < 16) ||
			(h->param.i_width % 16) || (h->param.i_height % 2)) {
		ijpege_log(h, C_LOG_ERROR, "invalid width x height (%dx%d)\n",
				h->param.i_width, h->param.i_height);
		goto err_resolution;
	}

	if ((h->param.i_csp != C_CSP_NV12) && (h->param.i_csp != C_CSP_T420)) {
		ijpege_log(h, C_LOG_ERROR, "invalid final encode color space:%d (only support NV12"
			" or T420)\n", h->param.i_csp);
		goto err_csp;
	}

	if (!h->param.b_user_fenc && ((h->param.ckmalloc == NULL)
				|| (h->param.ckfree == NULL) || (h->param.ckvirt_to_phys == NULL)
				|| (h->param.ckphys_to_virt == NULL))) {
		h->param.ckmalloc = c_malloc;
		h->param.ckfree = c_free;
		h->param.ckvirt_to_phys = c_virt_to_phys;
		h->param.ckphys_to_virt = c_phys_to_virt;
	}  else if ((h->param.ckmalloc == NULL) || (h->param.ckfree == NULL)
		 || (h->param.ckvirt_to_phys == NULL) || (h->param.ckphys_to_virt == NULL)){
		ijpege_log(h, C_LOG_ERROR, "invalidate memory manage func\n");
		goto err_invalidate_mm_func;
	}

	return 0;
#define BOOLIFY(x) h->param.x = !!h->param.x
	BOOLIFY( b_tlb );
	BOOLIFY( b_use_phy );
	BOOLIFY( b_user_fenc );
#undef BOOLIFY

err_invalidate_mm_func:
err_csp:
err_resolution:
err_soc:
	return -1;
}

ijpege_t *ijpege_init(ijpege_param_t *param)
{
	ijpege_t *h = NULL;

	if ((h = malloc(sizeof(*h))) == NULL) {
		ijpege_log(NULL, C_LOG_ERROR, "malloc ijpege_t var h failed:%s\n", strerror(errno));
		goto err_malloc_h;
	}
	memset(h, 0, sizeof(*h));
	memcpy(&h->param, param, sizeof(ijpege_param_t));

	if (ijpege_validate_parameters(h) < 0) {
		ijpege_log(NULL, C_LOG_ERROR, "ijpege_validate_parameters failed\n");
		goto err_validate_parameters;
	}

	h->fenc = &h->encframe;
	h->fenc->i_csp = h->param.i_csp;
	h->fenc->i_plane = 2;
	h->fenc->i_width[0] = h->fenc->i_width[1] = h->param.i_width;
	h->fenc->i_height[0] = h->param.i_height;
	h->fenc->i_height[1] = (h->param.i_height >> 1);

	c_mc_init(&h->mc);

	h->out.i_bitstream = C_ALIGN(h->param.i_width, 16) * C_ALIGN(h->param.i_height, 16) * 3 / 2;
	if (h->param.b_user_nalbuffer == 0) {
		if ((h->out.p_bitstream = memalign(C_NATIVE_ALIGN, h->out.i_bitstream)) == NULL) {
			ijpege_log(NULL, C_LOG_ERROR, "memalign p_bitstream failed\n");
			goto err_p_bitstream;
		}
		h->out.nal.p_payload = h->out.p_bitstream;
	}
	h->out.nal.i_payload = 0;

	if (ijpege_icodec_init(h) < 0) {
		ijpege_log(NULL, C_LOG_ERROR, "icodec_init failed\n");
		goto err_icodec_init;
	}

	if (ijpege_icodec_request_vbinfo(h)) {
		ijpege_log(NULL, C_LOG_ERROR, "icodec_get_vbinfo failed\n");
		goto err_icodec_request_vbinfo;
	}
    if (ijpege_pthread_mutex_init(&h->reconfig.mutex_rcfg, NULL) < 0) {
		ijpege_log(h, C_LOG_ERROR, "pthread_mutex_init failed\n");
		goto err_rcfg_mutex_init;
	}
	return h;

err_rcfg_mutex_init:
err_icodec_request_vbinfo:
	ijpege_icodec_deinit(h);
err_icodec_init:
err_p_bitstream:
	if (h->param.b_user_nalbuffer == 0) {
		free(h->out.p_bitstream);
	}
err_validate_parameters:
	free(h);
err_malloc_h:
	return NULL;
}

void ijpege_deinit(ijpege_t *h)
{
    ijpege_pthread_mutex_destroy(&h->reconfig.mutex_rcfg);
	ijpege_icodec_release_vbinfo(h);
	ijpege_icodec_deinit(h);
	if (h->param.b_user_nalbuffer == 0) {
		free(h->out.p_bitstream);
	}
	free(h);
}

int ijpege_encode(ijpege_t *h, ijpege_nal_t **pp_nal, int *pi_nal,
		ijpege_pic_t *pic_in, ijpege_pic_t *pic_out)
{
	if (!h || !pp_nal || !pi_nal || !pic_in) {
		ijpege_log(h, C_LOG_ERROR, "%s is NULL\n",
				(h==NULL)?"h":((pp_nal==NULL)?"pp_nal":(pi_nal==NULL?"pi_nal":"pic_in")));
		goto err_invalide_param;
	}

	*pp_nal = NULL;
	*pi_nal = 0;

	if (ijpege_update_fenc(h, h->fenc, pic_in) < 0) {
		ijpege_log(h, C_LOG_ERROR, "ijpege_update_fenc failed\n");
		goto err_update_fenc;
	}

	h->out.nal.i_payload = 0;
	*pp_nal = NULL;
	*pi_nal = 0;
	ijpege_write_header(&h->out.nal, h->param.i_width, h->param.i_height, h->fenc->i_ql,h->reconfig.user_ql_en, h->reconfig.qmem_table);

	if (ijpege_icodec_enc(h) < 0) {
		ijpege_log(h, C_LOG_ERROR, "ijpege_icodec_enc failed\n");
		goto err_icodec_enc;
	}

	if (h->out.i_bitstream >= (h->out.nal.i_payload + h->icodec_io.bslen + 4)) {
		memcpy(h->out.nal.p_payload + h->out.nal.i_payload, h->fbs, h->icodec_io.bslen);
		h->out.nal.i_payload += h->icodec_io.bslen;
		ijpege_write_tail(&h->out.nal);
	} else {
#ifdef CONFIG_SINGLE_BS
		hwicodec_bsbuf_unlock();
#endif
		ijpege_log(h, C_LOG_ERROR, "Error: i_bitstream size %d is too small to fill the jpeg bitstream %d\n", h->out.i_bitstream, h->icodec_io.bslen);
		goto err_bitstream_too_small;
	}

	*pp_nal = &h->out.nal;
	*pi_nal = 1;
#ifdef CONFIG_SINGLE_BS
	hwicodec_bsbuf_unlock();
#endif

	return 0;

err_bitstream_too_small:
err_icodec_enc:
err_update_fenc:
err_invalide_param:
	return -1;
}

int ijpege_get_param(ijpege_t *h, int param_id, const void *param)
{
    int rv = 0;
    switch (param_id)
    {
        case IJPEGE_RCFG_QL_ID:
             rv = ijpege_reconfig_ql_get(h, param);
             break;
        default:
             ijpege_log(h, C_LOG_ERROR, "error param id\n");
             rv = -1;
        break;
    }
    return rv;
}

int ijpege_set_param(ijpege_t *h, int param_id, const void *param)
{
    int rv = 0;
    switch (param_id)
    {
        case IJPEGE_RCFG_QL_ID:
             rv = ijpege_reconfig_ql_set(h, param);
             break;
        default:
             ijpege_log(h, C_LOG_ERROR, "error param id\n");
             rv = -1;
        break;
    }
    return rv;
}
